#include "threadPool.h"

int initialized = 0;
int cmd_pipe_fd[2];
int response_pipe_fd[2];
pthread_t main_thread_id;
pthread_mutex_t mResponse = PTHREAD_MUTEX_INITIALIZER;

void* threadpool_main_thread(void *ptr);

static int send_cmd(struct msg *c)
{
	if(sizeof(struct msg) != write(cmd_pipe_fd[1], c, sizeof(struct msg)))
	{
		fprintf(stderr, "write() failed to write %d bytes\n", sizeof(struct msg));
		return 1;
	}

	return 0;
}

static int recv_response(void *ptr, const int size)
{
	int count;
	count = read(response_pipe_fd[0], ptr, size);

	if(count == -1)
	{
		fprintf(stderr, "read() failed, pipe broken? aborting");
		abort();
	}
	if(count != size)
	{
		fprintf(stderr, "read() failed error : %s\n", strerror(errno));
		return 1;
	}
	return 0;
}

static int lock(void)
{
	pthread_mutex_lock(&mResponse);
    return 0;
}
static int unlock(void)
{
	pthread_mutex_unlock(&mResponse);
    return 0;
}
int ThreadPool_Exit(void)
{
	struct msg m;

    memset(&m, 0, sizeof(struct msg));
	m.cmd = CMD_EXIT;
    lock();
        send_cmd(&m);
        pthread_join(main_thread_id, NULL);
        /*Close all the pipes*/
        close(cmd_pipe_fd[0]);
        close(cmd_pipe_fd[1]);
        close(response_pipe_fd[0]);
        close(response_pipe_fd[1]);
        initialized = 0;
    unlock();
    pthread_mutex_destroy(&mResponse);
    memset(&mResponse, 0, sizeof(mResponse));

    return 0;
}

int ThreadPool_GetThreadPoolSize(void)
{
	int count;
	struct msg m;
    memset(&m, 0, sizeof(struct msg));
	m.cmd = CMD_GET_THREAD_POOL_SIZE;

    lock();
        send_cmd(&m);
        recv_response(&count, sizeof(int));
    unlock();

	return count;
}

int ThreadPool_SetThreadPoolSize(int count)
{
	struct msg m;
    memset(&m, 0, sizeof(struct msg));
	m.cmd = CMD_SET_THREAD_POOL_SIZE;
	m.arg1 = count;

    /* lock is not required here as we don't expect any response
     * from server*/
	send_cmd(&m);
	return count;
}

int ThreadPool_Ping(void)
{
	int count;
	struct msg m;
    memset(&m, 0, sizeof(struct msg));
	m.cmd = CMD_PING;
    m.arg1 = 0xF0E1D2C3;
    lock();
        send_cmd(&m);
        recv_response(&count, sizeof(int));
    unlock();

    if(count != m.arg1)
    {
        fprintf(stderr, "Ping() failed to return the correct value");
        return 1;
    }

	return 0;
}
int ThreadPool_Initialize(int num_of_threads)
{
	int ret, count;
	struct msg m;
	ThreadPool_MasterControl *t_mc = NULL;

	if(initialized != 0)
		return 0;
	t_mc = (ThreadPool_MasterControl*)malloc(sizeof(ThreadPool_MasterControl));
	t_mc->num_of_threads = num_of_threads;
	if(0 != pipe(t_mc->cmd_pipe_fd))
	{
		free(t_mc);
		fprintf(stderr, "pipe() syscall failed with error : %s\n", strerror(errno));
		return 1;
	}
	if(0 != pipe(t_mc->response_pipe_fd))
	{
		close(t_mc->cmd_pipe_fd[0]);
		close(t_mc->cmd_pipe_fd[1]);
		free(t_mc);
		fprintf(stderr, "pipe() syscall failed with error : %s\n", strerror(errno));
		return 1;
	}

	pthread_mutex_init(&mResponse, NULL);
	ret = pthread_create(&main_thread_id, NULL, &threadpool_main_thread, (void *)t_mc);
	if(0 != ret)
	{
		printf("pthread_create() failed : %s\n", strerror(errno));
		close(t_mc->cmd_pipe_fd[0]);
		close(t_mc->cmd_pipe_fd[1]);
		close(t_mc->response_pipe_fd[0]);
		close(t_mc->response_pipe_fd[1]);
		free(t_mc);
		return 1;
	}

	cmd_pipe_fd[0] = t_mc->cmd_pipe_fd[0];
	cmd_pipe_fd[1] = t_mc->cmd_pipe_fd[1];

	response_pipe_fd[0] = t_mc->response_pipe_fd[0];
	response_pipe_fd[1] = t_mc->response_pipe_fd[1];
	initialized = 1;

    memset(&m, 0, sizeof(struct msg));
	m.cmd = CMD_PING;
	m.arg1 = 0xF1F0F3F4;

	pthread_mutex_lock(&mResponse);
	send_cmd(&m);
	recv_response(&count, 4);
	pthread_mutex_unlock(&mResponse);
	if(count == m.arg1)
	{
		printf("Server is up and running\n");
	}
	else
	{
		fprintf(stderr, "server is not up\n");
	}
	return 0;
}
